package com.scientific.research.gobang.service.user.impl;

import cn.hutool.crypto.digest.DigestUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.scientific.research.gobang.mapper.RankingMapper;
import com.scientific.research.gobang.mapper.UserMapper;
import com.scientific.research.gobang.pojo.RankUser;
import com.scientific.research.gobang.pojo.User;
import com.scientific.research.gobang.utils.JwtUtils;
import com.scientific.research.gobang.service.SendEmailService;
import com.scientific.research.gobang.service.UserService;
import com.scientific.research.gobang.utils.JsonUtils;
import com.scientific.research.gobang.utils.RedisUtil;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;


@Service
public class UserServiceImpl implements UserService {
    @Autowired
    UserMapper userMapper;
    @Autowired
    RankingMapper rankingMapper;
    @Autowired
    SendEmailService sendEmailService;
    @Autowired
    RedisUtil redisUtil;
    @Autowired
    RedissonClient redissonClient;

    //返回用户信息
    @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
    @Override
    public User LodeUserMessage(Map<String, String> map) {
        if (JwtUtils.checkJWT(map.get("userToken")) == null) {
            return null;//如果查不到说明没登录
        } else {//查到了返回解析后的User
            return JsonUtils.toBean(JsonUtils.toString(JwtUtils.checkJWT(map.get("userToken")).get("user")), User.class);
        }
    }

    //发送邮箱，并保存相关信息
    @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
    @Override
    public void SendEmail(User user) {
        redisUtil.set(user.getEmail() + "_keyWord", sendEmailService.toSendEmail(user.getEmail()));
    }

    //验证邮箱正确性
    @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
    @Override
    public Map EmailVerify(Map jsonResponse) {
        if (redisUtil.get(jsonResponse.get("result") + "_IsExist") == null) {
            jsonResponse.put("result", "fail");
        } else {
            jsonResponse.put("result", "success");
        }
        return jsonResponse;
    }

    //登录认证
    @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
    @Override
    public Map LoginVerify(User user) {
        HashMap<String, String> map = new HashMap<>();
        //账号不存在直接返回
        if (redisUtil.get(user.getEmail() + "_IsExist") == null) {
            map.put("result", "EmailError");
        }
        //密码通过Bcrypt校验，如果密码不正确也直接返回
        else if (!DigestUtil.bcryptCheck(user.getPassword(), (String) redisUtil.get(user.getEmail() + "_IsExist"))) {
            map.put("result", "PasswordError");
        }
        //查询成功，将数据库里用户的信息放进token
        else {
            HashMap<String, Object> selectEmail = new HashMap<>();
            selectEmail.put("email", user.getEmail());
            User backUser = userMapper.selectByMap(selectEmail).get(0);
            backUser.setPassword("");//密码设为空，不能放进token
            backUser.setCode("");//代码设为空，个人代码不能放进token
            map.put("userToken", JwtUtils.getUserToken(backUser));//token有效期为一天
            map.put("result", "success");
        }
        return map;
    }

    //保存用户填写的个人信息
    @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
    @Override
    public Map SaveUserMessage(User saveUser) {
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        wrapper.eq("email", saveUser.getEmail());
        userMapper.update(saveUser, wrapper);
        //清空个人信息的Redis缓存
        if (redisUtil.get(saveUser.getEmail() + "_Message") != null) {
            redisUtil.del(saveUser.getEmail() + "_Message");
        }
        //返回新的userToken
        HashMap<String, String> map = new HashMap<>();
        map.put("userToken", JwtUtils.getUserToken(saveUser));//token有效期为一天
        return map;
    }

    //用于找回密码时的最后的提交验证
    @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
    @Override
    public Map SubmitChangePassword(Map jsonResponse) {
        User user = JsonUtils.toBean((String) jsonResponse.get("forgotUser"), User.class);
        String keyWord = (String) jsonResponse.get("keyWord");
        if (keyWord.equals(redisUtil.get(user.getEmail() + "_keyWord"))) {
            jsonResponse.put("result", "success");
            redisUtil.del(user.getEmail() + "_keyWord");
            User userUpdate = new User();
            //新密码同样采用Bcrypt强加密
            userUpdate.setPassword(DigestUtil.bcrypt(user.getPassword()));
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.eq("email", user.getEmail());
            userMapper.update(userUpdate, wrapper);
            //更新用户账号密码的Redis缓存
            redisUtil.set(user.getEmail() + "_IsExist", userUpdate.getPassword());
        } else {
            jsonResponse.put("result", "fail");
        }
        return jsonResponse;
    }

    //注册
    @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
    @Override
    public String Register(User newuser) {
        Map<String, Object> select_byName = new HashMap<>();
        select_byName.put("name", newuser.getName());
        //如果邮箱存在就注册失败
        if (redisUtil.get(newuser.getEmail() + "_IsExist") != null) {
            return "1";
        }
        //如果昵称存在也注册失败
        else if (userMapper.selectByMap(select_byName).size() > 0) {
            return "2";
        } else {
            //用户信息存入user表，密码采用Bcrypt强摘要加密
            newuser.setPassword(DigestUtil.bcrypt(newuser.getPassword()));
            newuser.setCode("c++");//默认C++语言，提交代码后会自动修改
            userMapper.insert(newuser);
            //将用户排名存入rank_user表
            RankUser rankUser = new RankUser();
            rankUser.setUserEmail(newuser.getEmail());
            rankUser.setName(newuser.getName());
            rankUser.setRankFractions(0.00);
            rankUser.setRankValue("0/0");
            rankingMapper.insert(rankUser);
            //清除Redis中rank_user表的缓存
            redisUtil.del("gobang_rankUser");
            //将用户账号密码放入Redis缓存中，减轻登录认证时MySql的压力
            redisUtil.set(newuser.getEmail() + "_IsExist", newuser.getPassword());
            //将IsRobotCodeCompile放入redis
            redisUtil.set(newuser.getEmail() + "_IsRobotCodeCompile", 0);
            //将当前用户剩余运行的代码数放进redis
            redisUtil.set(newuser.getEmail() + "_runCode", 0);
            return "0";
        }
    }

    //查看其它用户信息
    @SuppressWarnings("AlibabaLowerCamelCaseVariableNaming")
    @Override
    public User GetLookOtherUser(String email) {
        User userResult = (User) redisUtil.get(email + "_Message");
        if (userResult == null) {
            RLock lock = redissonClient.getLock(email + "_Message");
            lock.lock(15, TimeUnit.SECONDS);// Redisson分布式锁,防止缓存击穿

            userResult = (User) redisUtil.get(email + "_Message");// 再从Redis获取一次，如果没获取到就从数据库查
            if (userResult == null) {
                Map<String, Object> selectEmail = new HashMap<>();
                selectEmail.put("email", email);
                userResult = userMapper.selectByMap(selectEmail).get(0);
                userResult.setCode("");
                userResult.setPassword("");
                //将查询结果放入Redis缓存中，以后查询就可以从缓存中读取
                redisUtil.set(email + "_Message", userResult);
                //解锁
                lock.unlock();
            }
        }

        return userResult;
    }
}
